<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>go 并发编程示例</title>
  
    <meta name="author" content="秀才">

    <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
    <!--[if lt IE 9]>
      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->

    <!-- Le styles -->
    <link href="/assets/twitter/stylesheets/bootstrap.min.css" type="text/css" rel="stylesheet" media="all">
<link href="/assets/twitter/stylesheets/style.css" type="text/css" rel="stylesheet" media="all">
 
    <link href="/assets/twitter/widgets/google_prettify/stylesheets/twitter-bootstrap.css" type="text/css" rel="stylesheet" media="all">

    <!-- Le fav and touch icons -->
  <!-- Update these with your own images
    <link rel="shortcut icon" href="images/favicon.ico">
    <link rel="apple-touch-icon" href="images/apple-touch-icon.png">
    <link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
    <link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
  -->
  </head>

  <body>

    <div class="navbar">
      <div class="navbar-inner">
        <div class="container">
          <a class="brand" href="/">秀才</a>
          <ul class="nav">
            
              


  <li><a href="/tags">标签</a></li>


            
              


  <li><a href="/categories">分类</a></li>


            
              


  <li><a href="/about">关于我</a></li>


            
          </ul>
        </div>
      </div>
    </div>

    <div class="container">
      <div class="content">
        <div class="page-header">
  <h1>go 并发编程示例 </h1>
</div>

<div class="row">
  <div class="span12">
    <blockquote>
<p>参考书籍：《Go语言圣经》</p>
</blockquote>

<h3>定时任务</h3>

<pre><code class="language-go">func demo(input chan interface{}) {
    t1 := time.NewTimer(time.Second * 5)
    t2 := time.NewTimer(time.Second * 10)

    for {
        select {
        case msg &lt;- input:
            println(msg)

        case &lt;-t1.C:
            println(&quot;5s timer&quot;)
            t1.Reset(time.Second * 5)

        case &lt;-t2.C:
            println(&quot;10s timer&quot;)
            t2.Reset(time.Second * 10)
        }
    }
}
</code></pre>

<h3>断续器</h3>

<pre><code class="language-go">func main(){
    ticker := time.NewTicker(time.Second)
	for t := range ticker.C {
		fmt.Println(&quot;ticker&quot;, t)
	}
}
</code></pre>

<h3>超时</h3>

<pre><code class="language-go">func main(){
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)

    select {
        case e1 := &lt;-ch1:
        //如果ch1通道成功读取数据，则执行该case处理语句
            fmt.Printf(&quot;1th case is selected. e1=%v&quot;,e1)
        case e2 := &lt;-ch2:
        //如果ch2通道成功读取数据，则执行该case处理语句
            fmt.Printf(&quot;2th case is selected. e2=%v&quot;,e2)
        case &lt;- time.After(2 * time.Second):
            fmt.Println(&quot;Timed out&quot;)
    }
}
</code></pre>

<h3>自定义定时器</h3>

<pre><code class="language-go">func main(){
    var t *time.Timer

    f := func(){
        fmt.Printf(&quot;Expiration time : %v.\n&quot;, time.Now())
        fmt.Printf(&quot;C`s len: %d\n&quot;, len(t.C))
    }

    t = time.AfterFunc(1*time.Second, f)
    //让当前Goroutine 睡眠2s，确保大于内容的完整
    //这样做原因是，time.AfterFunc的调用不会被阻塞。它会以一部的方式在到期事件来临执行我们自定义函数f。
    time.Sleep(2 * time.Second)
}
</code></pre>

<h3>使用时间控制停止ticker</h3>

<pre><code class="language-go">func main(){
    //初始化断续器,间隔2s
    var ticker *time.Ticker = time.NewTicker(1 * time.Second)

    go func() {
        for t := range ticker.C {
            fmt.Println(&quot;Tick at&quot;, t)
        }
    }()

    time.Sleep(time.Second * 5)   //阻塞，则执行次数为sleep的休眠时间/ticker的时间
    ticker.Stop()       //停止ticker
    fmt.Println(&quot;Ticker stopped&quot;)
}
</code></pre>

<h3>并发读取文件夹所有文件的长度（使用通道）</h3>

<pre><code class="language-go">func test1() {
    // 遍历文件夹，获取文件名
	dir := &quot;./db&quot;
	paths, err := ioutil.ReadDir(dir)
	check(err)
	var files []string
	for _, path := range paths {
		if !path.IsDir() {
			files = append(files, path.Name())
		}
	}

	type FileInfo struct {
		FileName string
		Len      int
		Err      error
	}
	way := make(chan FileInfo, 100)
	for _, file := range files {
		go func(fileName string) {
			content, err := ioutil.ReadFile(fmt.Sprintf(&quot;%s/%s&quot;, dir, fileName))
            // 无论对错，都交给通道收取方
			way &lt;- FileInfo{
				FileName: fileName,
				Len:      len(content),
				Err:      err,
			}
		}(file)
	}

    // 我们知道需要从通道中取多少条数据
	for i := 0; i &lt; len(files); i++ {
		fileInfo := &lt;-way
		if fileInfo.Err != nil {
			fmt.Printf(&quot;[err] file:%s, err: %s \n&quot;, fileInfo.FileName, fileInfo.Err.Error())
			return
		}
		fmt.Printf(&quot;file:%s, len: %d \n&quot;, fileInfo.FileName, fileInfo.Len)
	}
}
</code></pre>

<h3>并发读取文件夹所有文件的长度（使用sync.WaitGroup）</h3>

<p>这个使用的其实就是计数器的道理</p>

<pre><code class="language-go">func test1() {
	dir := &quot;./db&quot;
	paths, err := ioutil.ReadDir(dir)
	check(err)
	var files []string
	for _, path := range paths {
		if !path.IsDir() {
			files = append(files, path.Name())
		}
	}

    // sizes用来计算总长度和阻塞直至所有goroutine走完
	sizes := make(chan int64)
    // 计数器
	var wg sync.WaitGroup
	for _, file := range files {
		wg.Add(1)
		go func(fileName string) {
			defer wg.Done()
			fileInfo, _ := os.Stat(fmt.Sprintf(&quot;%s/%s&quot;, dir, fileName))
			sizes &lt;- fileInfo.Size()
		}(file)
	}
	go func() {
		wg.Wait()
		close(sizes)
	}()
	var total int64
	for size := range sizes {
		fmt.Printf(&quot;size:%d\n&quot;, size)
		total += size
	}
	fmt.Printf(&quot;total size: %d\n&quot;, total)
}
</code></pre>

<h3>令牌-另一种信号计数器</h3>

<pre><code class="language-go">func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	// 链接列表
	workList := make(chan []string)
	// 存储已经处理的链接，使用map是为了去重
	seen := make(map[string]bool)
	// n是为了保证爬虫一直爬
	n := 1
	// 初始化 
	go func() { workList &lt;- []string{&quot;http://www.jianshu.com&quot;} }()
	for i := n; i &gt; 0; i++ {
		list := &lt;-workList
		for _, link := range list {
			if !seen[link] {
				seen[link] = true
				n++
				go func(link string) {
					workList &lt;- crawl(link)
				}(link)

			}
		}
	}
}

var tokens = make(chan struct{}, 20)

// 获取网页的链接  通过令牌
func crawl(url string) (list []string) {
	fmt.Println(url)
	tokens &lt;- struct{}{}
	list, err := Extract(url)
	&lt;-tokens
	if err != nil {
		log.Print(err)
		return
	}
	return
}

// 获取网页的链接
func Extract(url string) (list []string, err error) {
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf(&quot;getting %s, err %s&quot;, url, err.Error())
	}
	doc, err := html.Parse(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, fmt.Errorf(&quot;parsing %s as HTML, err %s&quot;, url, err.Error())
	}
	visitNode := func(n *html.Node) {
		if n.Type == html.ElementNode &amp;&amp; n.Data == &quot;a&quot; {
			for _, a := range n.Attr {
				if a.Key != &quot;href&quot; {
					continue
				}
				link, err := resp.Request.URL.Parse(a.Val)
				if err != nil {
					continue
				}
				list = append(list, link.String())
			}

		}
	}
	// 将函数作为参数传入（匿名函数）
	forEachNode(doc, visitNode, nil)
	return
}

func forEachNode(n *html.Node, pre, next func(node *html.Node)) {
	if pre != nil {
		pre(n)
	}
	for n := n.FirstChild; n != nil; n = n.NextSibling {
		forEachNode(n, pre, next)
	}
	if next != nil {
		next(n)
	}

}

</code></pre>

    <hr>
    <div class="pagination">
      <ul>
        <ul>
          
            <li class="prev"><a href="/go/go-%E6%A0%87%E5%87%86%E5%8C%85%EF%BC%9Async/" title="go 标准包：sync">&larr; Previous</a></li>
          
          

            <li><a href="/archive">Archive</a></li>

          
            <li class="next"><a href="/mongodb/mongodb-%E5%AE%89%E8%A3%85%E8%BF%90%E8%A1%8C/" title="mongodb 安装运行">Next &rarr;</a></li>
          
          
        </ul>
      </ul>
    </div>
    <hr>
    
  </div>
  
  <!-- <div class="span4">
    <h4>Published</h4>
    <div class="date"><span>2016-05-08</span></div>
    <br>
    <h4>Categories</h4>
    <ul class="tag_box">
    
      <li>
  <a href="/categories/#go-ref">go <span>7</span></a>
</li>
    
    </ul>
    <br>
    <h4>Tags</h4>
    <ul class="tag_box">
    
      <li>
  <a href="/tags/#go-ref">go <span>7</span></a>
</li>
    
      <li>
  <a href="/tags/#%E5%B9%B6%E5%8F%91-ref">并发 <span>6</span></a>
</li>
    
    </ul>
  </div> -->
</div>

      </div>

      <footer>
        <p>&copy; 秀才 2013 
          with help from <a href="http://github.com/wendal/gor" target="_blank" title="Gor -- Fast Blog">Gor</a>
          and <a href="http://twitter.github.com/bootstrap/" target="_blank">Twitter Bootstrap</a>
		  and Idea from <a href="http://ruhoh.com" target="_blank" title="The Definitive Technical Blogging Framework">ruhoh</a>
        </p>
      </footer>

    </div> <!-- /container -->
    <!-- <script src="//cdnjscn.b0.upaiyun.com/libs/prettify/r298/prettify.min.js"></script> -->
    <script src="http://cdn.staticfile.org/prettify/r298/prettify.min.js"></script>
    <script>
      var pres = document.getElementsByTagName("pre");
      for (var i=0; i < pres.length; ++i) {
        pres[i].className = "prettyprint linenums";
      }
      prettyPrint();
    </script>
    
    
  </body>
</html>
